package com.qen.repository;

import cn.hutool.core.lang.Console;
import cn.hutool.core.util.StrUtil;
import com.qen.annotation.LogicDel;
import com.qen.common.BeetlUtils;
import com.qen.common.GenericTypeUtils;
import com.qen.common.LogicInfoUtils;
import com.qen.common.TableInfoHelper;
import com.qen.constant.DataBaseConstants;
import com.qen.enums.SqlKeyword;
import com.qen.wrapper.Wrapper;
import org.beetl.sql.clazz.ClassDesc;
import org.beetl.sql.clazz.NameConversion;
import org.beetl.sql.clazz.TableDesc;
import org.beetl.sql.clazz.kit.BeanKit;
import org.beetl.sql.clazz.kit.BeetlSQLException;
import org.beetl.sql.core.SQLManager;
import org.beetl.sql.core.SqlId;
import org.beetl.sql.core.page.DefaultPageRequest;
import org.beetl.sql.core.page.PageRequest;
import org.beetl.sql.core.page.PageResult;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

/**
 * @author licz
 * @data 2025年05月14日 3:12 PM
 */
public class BaseImpl<T> implements BaseDao<T> {

    private volatile SQLManager sqlManager;

    private SQLManager getSqlManager() {
        if (this.sqlManager == null) {
            sqlManager = BeetlUtils.getSQLManager();
        }
        return this.sqlManager;
    }

    /**
     * T 对象的 库更新，obj中需要主键，没有就不更新
     *
     * @param obj
     */
    @Override
    public int updateIfNotNull(Map<String, Object> obj) {
        return getSqlManager().updateTemplateById(
                GenericTypeUtils.getGenericType(getClass().getGenericSuperclass()),
                obj);
    }

    /**
     * 根据主键删除T对象.
     *
     * @param key 主键
     */
    @Override
    public int deleteEntityByKey(Serializable key) {
        return getSqlManager().deleteById(
                GenericTypeUtils.getGenericType(getClass().getGenericSuperclass()),
                key);
    }

    @Override
    public <E> int deleteEntityByKey(List<? extends Serializable> keys, Class<E> clz) {
        if (clz == null) {
            clz = GenericTypeUtils.getGenericType(getClass().getGenericSuperclass());
        }
        ClassDesc desc = getSqlManager().getClassDesc(clz);
        String idKey = desc.getIdAttr();
        Map<String, Object> par = new HashMap<>();
        par.put(idKey, keys);
        return deleteByMap(par);
    }

    @Override
    public int[] updateBatchIfNotNull(List<T> list) {
        return getSqlManager().updateBatchTemplateById(
                GenericTypeUtils.getGenericType(getClass().getGenericSuperclass()),
                list);
    }

    @Override
    public <E> int[] addBatch(List<E> list) {
        return getSqlManager().insertBatch(
                GenericTypeUtils.getGenericType(getClass().getGenericSuperclass()),
                list);
    }

    @Override
    public <E> Long selectCount(Wrapper<T> queryWrapper, Class<E> clz) {
        TableDesc table = TableInfoHelper.getTableInfo(clz);
        StringBuilder sql = new StringBuilder()
                .append(SqlKeyword.SELECT.getSqlSegment())
                .append(StrUtil.SPACE)
                .append(SqlKeyword.COUNT.getSqlSegment())
                .append(StrUtil.SPACE)
                .append(SqlKeyword.FROM.getSqlSegment())
                .append(StrUtil.SPACE)
                .append(table.getName());
        if (StrUtil.isNotBlank(queryWrapper.getAlias())) {
            sql.append(SqlKeyword.AS.getSqlSegment())
                    .append(queryWrapper.getAlias());
        }
        if (StrUtil.isNotBlank(queryWrapper.getFrom())) {
            sql.append(StrUtil.SPACE)
                    .append(queryWrapper.getFrom());
        }
        if (queryWrapper.isNonEmptyOfNormal()) {
            sql.append(StrUtil.SPACE)
                    .append(SqlKeyword.WHERE.getSqlSegment())
                    .append(StrUtil.SPACE)
                    .append(queryWrapper.getTargetSql());
        }

        if (StrUtil.isNotBlank(queryWrapper.getSqlComment())) {
            Console.log("SQL执行Comment:{}", queryWrapper.getSqlComment());
        }
        List<Long> list = getSqlManager().execute(sql.toString(), Long.class, queryWrapper.getParam());
        if (list.isEmpty()) {
            return 0L;
        } else if (list.size() > 1) {
            //仅在group by 时存在多条记录；此时使用总记录数
            return (long) list.size();
        } else {
            return list.get(0);
        }
    }

    @Override
    public <E, P extends PageRequest<E>> PageResult<E> selectList(P page, Wrapper<T> queryWrapper, Class<E> clz) {
        TableDesc table = TableInfoHelper.getTableInfo(clz);
        if (table == null) {
            table = TableInfoHelper.getTableInfo(GenericTypeUtils.getGenericType(getClass().getGenericSuperclass()));
        }
        StringBuilder sql = new StringBuilder();
        if (StrUtil.isNotBlank(queryWrapper.getSqlFirst())) {
            sql.append(queryWrapper.getSqlFirst());
        } else {
            sql.append(SqlKeyword.SELECT.getSqlSegment()).append(StrUtil.SPACE);
            if (queryWrapper.getSqlSelect() == null) {
                sql.append(StrUtil.join(",", table.getCols()));
            } else {
                sql.append(queryWrapper.getSqlSelect());
            }
        }
        sql.append(StrUtil.SPACE).append(SqlKeyword.FROM.getSqlSegment()).append(StrUtil.SPACE)
                .append(table.getName())
                .append(StrUtil.SPACE);
        if (StrUtil.isNotBlank(queryWrapper.getAlias())) {
            sql.append(SqlKeyword.AS.getSqlSegment())
                    .append(queryWrapper.getAlias());
        }
        if (StrUtil.isNotBlank(queryWrapper.getFrom())) {
            sql.append(StrUtil.SPACE)
                    .append(queryWrapper.getFrom());
        }
        Map<String, Object> param = queryWrapper.getParam();
        if (queryWrapper.isNonEmptyOfNormal()) {
            sql.append(SqlKeyword.WHERE.getSqlSegment())
                    .append(StrUtil.SPACE)
                    .append(queryWrapper.getTargetSql());
        }
        if (StrUtil.isNotBlank(queryWrapper.getSqlComment())) {
            Console.log("SQL执行Comment:{}", queryWrapper.getSqlComment());
        }
        if (page != null) {
            int pageSize = page.getPageSize();
//            long pageNumber = page.getPageNumber();
//            boolean totalRequired = page.isTotalRequired();
            Object offset = page.getStart(true);
            String start = SqlKeyword.LIMIT_START.getSqlSegment();
            String size = SqlKeyword.LIMIT_SIZE.getSqlSegment();
            sql.append(StrUtil.SPACE)
                    .append(SqlKeyword.LIMIT.getSqlSegment())
                    .append(StrUtil.SPACE)
                    .append(DataBaseConstants.HASH_LEFT_BRACE)
                    .append(start)
                    .append(StrUtil.DELIM_END)
                    .append(StrUtil.COMMA)
                    .append(DataBaseConstants.HASH_LEFT_BRACE)
                    .append(size)
                    .append(StrUtil.DELIM_END);
            param.put(start, offset);
            param.put(size, pageSize);
        } else {
            page = (P) DefaultPageRequest.of(0, 0, false);
        }
        List<E> list = getSqlManager().execute(sql.toString(), clz, param);
        if (page.isTotalRequired()) {
            return page.of(list, selectCount(queryWrapper));
        } else {
            return page.of(list);
        }
    }

    @Override
    public int delete(Wrapper<T> queryWrapper) {
        Class<?> clz = GenericTypeUtils.getGenericType(getClass().getGenericSuperclass());
        TableDesc desc = TableInfoHelper.getTableInfo(clz);
        Field field = LogicInfoUtils.getFieldByPojo(clz);
        int i = 0;
        if (field == null) {
            StringBuilder sql = new StringBuilder()
                    .append(SqlKeyword.DELETE.getSqlSegment());
            if (StrUtil.isNotBlank(queryWrapper.getAlias())) {
                sql.append(StrUtil.SPACE)
                        .append(queryWrapper.getAlias());
            }
            sql.append(StrUtil.SPACE)
                    .append(SqlKeyword.FROM.getSqlSegment())
                    .append(StrUtil.SPACE)
                    .append(desc.getName());
            if (StrUtil.isNotBlank(queryWrapper.getAlias())) {
                sql.append(SqlKeyword.AS.getSqlSegment())
                        .append(queryWrapper.getAlias());
            }
            if (StrUtil.isNotBlank(queryWrapper.getFrom())) {
                sql.append(StrUtil.SPACE)
                        .append(queryWrapper.getFrom());
            }
            sql.append(SqlKeyword.WHERE.getSqlSegment())
                    .append(StrUtil.SPACE)
                    .append(queryWrapper.getWhere());
            i = getSqlManager().executeUpdate(sql.toString(), queryWrapper.getParam());
        } else {
            StringBuilder sql = new StringBuilder()
                    .append(SqlKeyword.UPDATE.getSqlSegment())
                    .append(desc.getName());
            if (StrUtil.isNotBlank(queryWrapper.getAlias())) {
                sql.append(StrUtil.SPACE)
                        .append(queryWrapper.getAlias());
            }
            if (StrUtil.isNotBlank(queryWrapper.getFrom())) {
                sql.append(StrUtil.SPACE)
                        .append(queryWrapper.getFrom());
            }
            sql.append(SqlKeyword.SET.getSqlSegment());
            if (StrUtil.isNotBlank(queryWrapper.getAlias())) {
                sql.append(StrUtil.SPACE)
                        .append(queryWrapper.getAlias())
                        .append(StrUtil.DOT);
            }
            String col = getSqlManager().getNc().getColName(clz, field.getName());
            String delStatus = field.getAnnotation(LogicDel.class).deleteStatus();
            sql.append(col).append(SqlKeyword.EQ.getSqlSegment())
                    .append(delStatus)
                    .append(SqlKeyword.WHERE.getSqlSegment())
                    .append(queryWrapper.getWhere());
            i = getSqlManager().executeUpdate(sql.toString(), queryWrapper.getParam());
        }
        return i;
    }

    @Override
    public int update(T entity, Wrapper<T> wrapper) {
        Class<?> clz = wrapper.getEntityClass();
        if (clz == null && entity == null) {
            clz = GenericTypeUtils.getGenericType(getClass().getGenericSuperclass());
        } else if (entity != null) {
            clz = entity.getClass();
        }

        NameConversion nc = getSqlManager().getNc();
        String tableName = nc.getTableName(clz);
        StringBuilder sql = new StringBuilder()
                .append(SqlKeyword.UPDATE.getSqlSegment())
                .append(StrUtil.SPACE)
                .append(tableName);
        String alias = wrapper.getAlias();
        if (StrUtil.isNotBlank(alias)) {
            sql.append(StrUtil.SPACE).append(alias);
        }
        if (StrUtil.isNotBlank(wrapper.getFrom())) {
            sql.append(StrUtil.SPACE).append(wrapper.getFrom());
        }
        sql.append(StrUtil.SPACE)
                .append(SqlKeyword.SET.getSqlSegment())
                .append(StrUtil.SPACE);
        ClassDesc classDesc = getSqlManager().getClassDesc(clz);
        Map<String, Object> param = wrapper.getParam();
        if (entity != null) {
            Class<?> finalClz = clz;
            StringBuilder set = new StringBuilder();
            classDesc.getAttrs().forEach(attr -> {
                String colName = nc.getColName(finalClz, attr);
                Object val = BeanKit.getBeanProperty(entity, attr);
                if (val != null) {
                    if (StrUtil.isNotBlank(alias)) {
                        set.append(alias).append(StrUtil.DOT);
                    }
                    set.append(colName)
                            .append(SqlKeyword.EQ.getSqlSegment())
                            .append(DataBaseConstants.HASH_LEFT_BRACE)
                            .append(attr)
                            .append(StrUtil.DELIM_END)
                            .append(StrUtil.COMMA)
                    ;
                    param.put(attr, val);
                }
            });
            String setStr = set.substring(0, set.length() - 1);
            sql.append(setStr);
        } else {
            String set = wrapper.getSqlSet();
            sql.append(set);
        }
        sql.append(StrUtil.SPACE)
                .append(SqlKeyword.WHERE.getSqlSegment())
                .append(StrUtil.SPACE)
                .append(wrapper.getWhere());
        return getSqlManager().executeUpdate(sql.toString(), param);
    }

    @Override
    public <E> E findObjectByKey(Serializable key, Class<E> clz) {
        return getSqlManager().single(clz, key);
    }

    public <E> List<E> findObjectByKey(List<? extends Serializable> keys, Class<E> clz) {
        return getSqlManager().selectByIds(clz, keys);
    }

    @Override
    public <E> E querySingleObject(String sql, Object params, Class<E> clz, boolean strictMode) {
        List<E> list = getSqlManager().execute(sql, clz, params);
        if (list.isEmpty()) {
            return null;
        } else if (strictMode) {
            throw new BeetlSQLException(BeetlSQLException.ID_EXPECTED_ONE_ERROR, "查询期望只返回一条结果集");
        } else {
            return list.get(0);
        }
    }

    @Override
    public List<Map<String, Object>> queryMapList(String sql, Map<String, Object> params) {
        return BeetlUtils.convertResult(getSqlManager().execute(sql, Map.class, params));
    }

    @Override
    public <E> List<E> queryEntityList(String sql, Object params, Class<E> clz) {
        return getSqlManager().execute(BeetlUtils.parseSqlTemplate(sql, params), clz, params);
    }

    @Override
    public <E> E querySingleObjectFromMarkdown(String fileName, String sqlMark, Map<String, Object> params, Class<E> clz) {
        SqlId sqlId = SqlId.of(fileName, sqlMark);
        List<E> list = getSqlManager().select(sqlId, clz, params);
        if (list.isEmpty()) {
            return null;
        } else if (list.size() > 1) {
            throw new BeetlSQLException(BeetlSQLException.ID_EXPECTED_ONE_ERROR, "查询期望只返回一条结果集");
        } else {
            return list.get(0);
        }
    }

    @Override
    public List<Map<String, Object>> queryMapListFromMarkDown(String fileName, String sqlMark, Map<String, Object> params) {
        SqlId sqlId = SqlId.of(fileName, sqlMark);
        List<Map> list = getSqlManager().select(sqlId, Map.class, params);
        return BeetlUtils.convertResult(list);
    }

    @Override
    public <E> List<E> queryEntityListFromMarkDown(String fileName, String sqlMark, Map<String, Object> params, Class<E> clz) {
        SqlId sqlId = SqlId.of(fileName, sqlMark);
        return getSqlManager().select(sqlId, clz, params);
    }

    @Override
    public <E> void deleteEntityByKey(Serializable key, Class<E> clazz) {
        getSqlManager().deleteById(clazz, key);
    }

    @Override
    public <E> Object saveEntity(E entity) {
        getSqlManager().upsert(entity);
        String idName = getSqlManager().getClassDesc(entity.getClass()).getIdAttr();
        return BeanKit.getBeanProperty(entity, idName);
    }

    @Override
    public <E> boolean[] saveBatch(List<E> list) {

        boolean[] flags = new boolean[list.size()];
        for (int i = 0; i < list.size(); i++) {
            E e = list.get(i);
            flags[i] = getSqlManager().upsert(e);
        }
        return flags;
    }

    @Override
    public <E> void upsertByKey(List<E> list, String[] uniqueKeyNames) {
        if (list.isEmpty()) {
            return;
        }
        Class<E> o = (Class<E>) list.get(0).getClass();
        String tableName = getSqlManager().getNc().getTableName(o);
        ClassDesc classDesc = getSqlManager().getClassDesc(o);

        String dbStyle = getSqlManager().getDbStyle().getName().toLowerCase(Locale.ROOT);
        if ("mysql".equals(dbStyle)) {
            BeetlUtils.mysqlInsertOrUpdate(uniqueKeyNames, tableName, classDesc, list.toArray());
        } else {
            list.stream().forEach(obj -> {
                upsertByKey(obj, uniqueKeyNames);
            });
        }
    }

    @Override
    public <E> void upsertByKey(E obj, String[] uniqueKeyNames) {
        String tableName = getSqlManager().getNc().getTableName(obj.getClass());
        ClassDesc classDesc = getSqlManager().getClassDesc(obj.getClass());
        String dbStyle = getSqlManager().getDbStyle().getName().toLowerCase(Locale.ROOT);
        if ("mysql".equals(dbStyle)) {
            BeetlUtils.mysqlInsertOrUpdate(uniqueKeyNames, tableName, classDesc, obj);
            return;
        }
        List<String> idProperties = classDesc.getIdAttrs();
        if (idProperties.size() != 1) {
            throw new BeetlSQLException(BeetlSQLException.ID_EXPECTED_ONE_ERROR, "upsertByKey方法期望只有一个主键");
        }
        String idColName = idProperties.get(0);//主键字段
        //拼装sql
        StringBuilder sql = new StringBuilder("select * from " + tableName + " where ");
        for (int i = 0; i < uniqueKeyNames.length; ++i) {
            String keyName = uniqueKeyNames[i];
            String colName = getSqlManager().getNc().getColName(obj.getClass(), keyName);
            Object pk = BeanKit.getBeanProperty(obj, keyName);
            if (pk == null) {
                throw new BeetlSQLException(BeetlSQLException.GET_OBJECT_PROPERTY_ERROR, "检查实体的实例，确保已对uniqueKeyNames中的字段进行赋值");
            }
            if (i != 0) {
                sql.append(StrUtil.SPACE)
                        .append(SqlKeyword.AND.getSqlSegment())
                        .append(StrUtil.SPACE);
            }
            sql.append(colName).append(StrUtil.SPACE)
                    .append(SqlKeyword.EQ.getSqlSegment())
                    .append(StrUtil.SPACE)
                    .append(StrUtil.COLON)
                    .append(keyName);
        }

        E selectResult = (E) querySingleObject(sql.toString(), obj, obj.getClass(), false);
        if (selectResult == null) {
            getSqlManager().insert(obj);
        } else {
            Object pk = BeanKit.getBeanProperty(obj, idColName);
            if (pk == null) {
                String idName = classDesc.getIdAttr();
                Object id = BeanKit.getBeanProperty(obj, idName);
                BeanKit.setBeanProperty(obj, id, idColName);
            }
            getSqlManager().updateTemplateById(obj);
//            sqlManager.updateById(obj);
        }
    }

    @Override
    public <E> void updateBatchIfNotNull(Class clazz, List<E> list) {
        getSqlManager().updateBatchTemplateById(clazz, list);
    }

    @Override
    public <E> int deleteEntity(E entity) {
        return getSqlManager().deleteObject(entity);
    }

    @Override
    public <E> Object addEntity(E entity) {
        getSqlManager().insert(entity.getClass(), entity);
        String idName = getSqlManager().getClassDesc(entity.getClass()).getIdAttr();
        return BeanKit.getBeanProperty(entity, idName);
    }

    @Override
    public <E> Object updateEntity(E entity) {
        getSqlManager().updateById(entity);
        String idName = getSqlManager().getClassDesc(entity.getClass()).getIdAttr();
        return BeanKit.getBeanProperty(entity, idName);
    }

    @Override
    public <E> int[] updateBatch(List<E> list) {
        return getSqlManager().updateByIdBatch(list);
    }

    @Override
    public <E> void updateIfNotNull(E entity) {
        getSqlManager().updateTemplateById(entity);
    }

    @Override
    public Integer sqlOperater(String sql, Map valuemap) {
        return getSqlManager().executeUpdate(BeetlUtils.parseSqlTemplate(sql, valuemap), valuemap);
    }

    @Override
    public int deleteJoin(Wrapper<T> wrapper) {
        return delete(wrapper);
    }

    @Override
    public int updateJoin(T entity, Wrapper<T> wrapper) {
        return update(entity, wrapper);
    }

    @Override
    public int updateJoinAndNull(T entity, Wrapper<T> wrapper) {
        Class<?> clz = wrapper.getEntityClass();
        if (clz == null && entity == null) {
            clz = GenericTypeUtils.getGenericType(getClass().getGenericSuperclass());
        } else if (entity != null) {
            clz = entity.getClass();
        }

        NameConversion nc = getSqlManager().getNc();
        String tableName = nc.getTableName(clz);
        StringBuilder sql = new StringBuilder()
                .append(SqlKeyword.UPDATE.getSqlSegment())
                .append(StrUtil.SPACE)
                .append(tableName);
        String alias = wrapper.getAlias();
        if (StrUtil.isNotBlank(alias)) {
            sql.append(StrUtil.SPACE).append(alias);
        }
        if (StrUtil.isNotBlank(wrapper.getFrom())) {
            sql.append(StrUtil.SPACE).append(wrapper.getFrom());
        }
        sql.append(StrUtil.SPACE)
                .append(SqlKeyword.SET.getSqlSegment())
                .append(StrUtil.SPACE);
        ClassDesc classDesc = getSqlManager().getClassDesc(clz);
        Map<String, Object> param = wrapper.getParam();
        if (entity != null) {
            Class<?> finalClz = clz;
            StringBuilder set = new StringBuilder();
            classDesc.getAttrs().forEach(attr -> {
                String colName = nc.getColName(finalClz, attr);
                Object val = BeanKit.getBeanProperty(entity, attr);
                if (StrUtil.isNotBlank(alias)) {
                    set.append(alias).append(StrUtil.DOT);
                }
                set.append(colName)
                        .append(SqlKeyword.EQ.getSqlSegment())
                        .append(DataBaseConstants.HASH_LEFT_BRACE)
                        .append(attr)
                        .append(StrUtil.DELIM_END)
                        .append(StrUtil.COMMA)
                ;
                param.put(attr, val);
            });
            String setStr = set.substring(0, set.length() - 1);
            sql.append(setStr);
        } else {
            String set = wrapper.getSqlSet();
            set = set.substring(0, set.length() - 1);
            sql.append(set);
        }
        sql.append(StrUtil.SPACE)
                .append(SqlKeyword.WHERE.getSqlSegment())
                .append(StrUtil.SPACE)
                .append(wrapper.getWhere());
        return getSqlManager().executeUpdate(sql.toString(), param);
    }

    @Override
    public Long selectJoinCount(Wrapper<T> wrapper) {
        return selectCount(wrapper);
    }

    @Override
    public <DTO, P extends PageRequest<DTO>> PageResult<DTO> selectJoinPage(P page, Class<DTO> clz, Wrapper<T> wrapper) {
        TableDesc table = TableInfoHelper.getTableInfo(wrapper.getEntityClass());
        StringBuilder sql = new StringBuilder();
        if (StrUtil.isNotBlank(wrapper.getSqlFirst())) {
            sql.append(wrapper.getSqlFirst());
        } else {
            sql.append(SqlKeyword.SELECT.getSqlSegment()).append(StrUtil.SPACE);
            if (wrapper.getSqlSelect() == null) {
                sql.append(StrUtil.join(",", table.getCols()));
            } else {
                sql.append(wrapper.getSqlSelect());
            }
        }
        sql.append(StrUtil.SPACE).append(SqlKeyword.FROM.getSqlSegment()).append(StrUtil.SPACE)
                .append(table.getName())
                .append(StrUtil.SPACE);
        if (StrUtil.isNotBlank(wrapper.getAlias())) {
            sql.append(SqlKeyword.AS.getSqlSegment())
                    .append(wrapper.getAlias());
        }
        if (StrUtil.isNotBlank(wrapper.getFrom())) {
            sql.append(StrUtil.SPACE)
                    .append(wrapper.getFrom());
        }
        if (wrapper.isNonEmptyOfNormal()) {
            sql.append(SqlKeyword.WHERE.getSqlSegment())
                    .append(StrUtil.SPACE)
                    .append(wrapper.getTargetSql());
        }
        if (StrUtil.isNotBlank(wrapper.getSqlComment())) {
            Console.log("SQL执行Comment:{}", wrapper.getSqlComment());
        }
        Map<String, Object> param = wrapper.getParam();

        if (page != null) {
            int pageSize = page.getPageSize();
//            long pageNumber = page.getPageNumber();
//            boolean totalRequired = page.isTotalRequired();
            Object offset = page.getStart(true);
            String start = SqlKeyword.LIMIT_START.getSqlSegment();
            String size = SqlKeyword.LIMIT_SIZE.getSqlSegment();
            sql.append(StrUtil.SPACE)
                    .append(SqlKeyword.LIMIT.getSqlSegment())
                    .append(StrUtil.SPACE)
                    .append(DataBaseConstants.HASH_LEFT_BRACE)
                    .append(start)
                    .append(StrUtil.DELIM_END)
                    .append(StrUtil.COMMA)
                    .append(DataBaseConstants.HASH_LEFT_BRACE)
                    .append(size)
                    .append(StrUtil.DELIM_END);
            param.put(start, offset);
            param.put(size, pageSize);
        }
        List<DTO> list = getSqlManager().execute(sql.toString(), clz, param);
        if (page.isTotalRequired()) {
            return page.of(list, selectJoinCount(wrapper));
        } else {
            return page.of(list);
        }
    }
}
